﻿using NewsSystem.ClientModels;
using NewsSystem.Contracts;
using NewsSystem.Models;
using NewsSystem.WebApi.Helpers;
using PubNubNotificator;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Web.Http;

namespace NewsSystem.WebApi.Controllers
{
    public class ArticlesController : BaseController
    {
        public ArticlesController(INewsUnitOfWork unitOfWork)
            : base(unitOfWork)
        {
        }

        public IQueryable<ArticleModel> Get(string sessionId, int count, bool myArticles)
        {
            var currentAuthor = this.Uow.User.GetBySessionId(sessionId);
            if (currentAuthor == null)
            {
                throw new ArgumentException("Invalid session id.");
            }

            var result = this.Uow.Article.GetAll();
            if (myArticles)
            {
                result = result.Where(a => a.Author.Id == currentAuthor.Id);
            }

            var articleModels = result.OrderByDescending(a=>a.CreationDate).Take(count).Select(a => new ArticleModel
             {
                 Title = a.Title,
                 Id = a.Id,
             });

            return articleModels;
        }

        public HttpResponseMessage Get(int id, string sessionId)
        {
            var responseMsg = this.PerformOperation(() =>
            {
                var currentAuthor = this.Uow.User.GetBySessionId(sessionId);
                if (currentAuthor == null)
                {
                    throw new ArgumentException("Invalid session id.");
                }

                Article article = this.Uow.Article.Get(id);
                ArticleModel result = new ArticleModel();
                result.Content = article.Content;
                result.Id = article.Id;
                result.Rating = article.Votes.Sum(v => v.Value);
                result.Title = article.Title;
                result.Comments = GetComments(article.Comments);
                result.Images = article.Images.Select(i => new ImageModel
                {
                    ImageId = i.ImageId,
                    Title = i.Title,
                    Url = i.Url
                }).ToList();

                var currentAuthorVote = article.Votes.FirstOrDefault(v => v.Author.Id == currentAuthor.Id);
                if (currentAuthorVote != null)
                {
                    result.VoteValue = currentAuthorVote.Value;
                }

                return result;
            });

            return responseMsg;
        }

        private ICollection<CommentModel> GetComments(ICollection<Comment> comments)
        {
            List<CommentModel> result = new List<CommentModel>();

            foreach (var comment in comments.OrderBy(c => c.CreationDate))
            {
                CommentModel current = new CommentModel
                {
                    AuthorNickname = comment.Author.NickName,
                    Id = comment.Id,
                    Content = comment.Content,
                    CreationDate = comment.CreationDate,
                };

                current.Comments = GetComments(comment.Comments);
                result.Add(current);
            }

            return result;
        }

        public HttpResponseMessage Post([FromBody] ArticleModel articleModel, string sessionId)
        {
            var responseMsg = this.PerformOperation(() =>
            {
                var currentAuthor = this.Uow.User.GetBySessionId(sessionId);
                if (currentAuthor == null)
                {
                    throw new ArgumentException("Invalid session id.");
                }

                Article article = new Article();
                article.Author = currentAuthor;
                article.Content = articleModel.Content;
                article.CreationDate = DateTime.Now;
                article.Title = articleModel.Title;
                foreach (var imageModel in articleModel.Images)
                {
                    var image = this.Uow.Image.Get(imageModel.ImageId);
                    article.Images.Add(image);
                }

                this.Uow.Article.Add(article);
                this.Uow.Commit();

                this.SendNotifications(article);
            });

            return responseMsg;
        }

        public HttpResponseMessage Put(int id, [FromBody] ArticleModel articleModel, string sessionId)
        {
            var responseMsg = this.PerformOperation(() =>
            {
                var currentAuthor = this.Uow.User.GetBySessionId(sessionId);
                if (currentAuthor == null)
                {
                    throw new ArgumentException("Invalid session id.");
                }

                Article article = UpdateArticle(articleModel);
                this.Uow.Article.Update(article.Id, article);
                this.Uow.Commit();
            });

            return responseMsg;
        }

        private Article UpdateArticle(ArticleModel articleModel)
        {
            Article article = this.Uow.Article.Get(articleModel.Id);
            article.Content = articleModel.Content;
            article.Title = articleModel.Title;

            var dict = new Dictionary<int, ImagePair>();
            foreach (var image in article.Images)
            {
                dict.Add(image.ImageId, new ImagePair(image, false));
            }

            foreach (var imageModel in articleModel.Images)
            {
                var imageEntity = this.Uow.Image.Get(imageModel.ImageId);
                if (dict.ContainsKey(imageModel.ImageId))
                {
                    dict[imageModel.ImageId].IsActual = true;
                }
                else
                {
                    dict.Add(imageEntity.ImageId, new ImagePair(imageEntity, true));
                }
            }

            HashSet<Image> entityImages = CreateActualImageCollection(dict);

            article.Images = entityImages;
            return article;
        }

        private HashSet<Image> CreateActualImageCollection(Dictionary<int, ImagePair> dict)
        {
            HashSet<Image> entityImages = new HashSet<Image>();
            foreach (var pair in dict)
            {
                if (pair.Value.IsActual)
                {
                    entityImages.Add(pair.Value.Image);
                }
                else
                {
                    this.Uow.Image.Delete(pair.Value.Image);
                }
            }
            return entityImages;
        }

        public void Delete(int id, string sessionId)
        {
            var currentAuthor = this.Uow.User.GetBySessionId(sessionId);
            if (currentAuthor == null)
            {
                throw new ArgumentException("Invalid session id.");
            }

            var article = this.Uow.Article.Get(id);
            // delete comments
            DeleteComments(article);
            // delete votes
            DeleteVotes(article);
            // delete images
            DeleteImages(article);
            // delete article
            this.Uow.Article.Delete(id);
            this.Uow.Commit();
        }

        private void DeleteImages(Article article)
        {
            foreach (var image in article.Images)
            {
                this.Uow.Image.Delete(image);
            }
        }

        private void DeleteVotes(Article article)
        {
            foreach (var vote in article.Votes)
            {
                this.Uow.Vote.Delete(vote);
            }
        }

        private void DeleteComments(Article article)
        {
            foreach (var comment in article.Comments)
            {
                DeleteSubComments(comment);
                this.Uow.Comment.Delete(comment);
            }
        }

        private void DeleteSubComments(Comment comment)
        {
            foreach (var subComment in comment.Comments)
            {
                DeleteSubComments(subComment);
            }

            this.Uow.Comment.Delete(comment);
        }

        private void SendNotifications(Article article)
        {
            ArticleNotificationModel notification = new ArticleNotificationModel
            {
                ArticleId = article.Id,
                AuthorNickname = article.Author.NickName,
                CreationDate = article.CreationDate,
                Title = article.Title
            };

            PubNubNotificationSender.Send(notification);
        }
    }
}